1:1 스터디 리뷰 통계·후기 목록 추가 및 그룹 스터디 수정 스키마 분리#441
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough1:1 리뷰 페이지를 클라이언트에서 직접 데이터 페칭하고 UI 조합하도록 재구성하고, 그룹 스터디 폼 스키마를 생성/편집 모드별로 분리·검증하도록 리팩토링했으며, 완료된 리뷰 모달 렌더링과 제출 성공 흐름을 통합·단순화했습니다. Changes
Sequence DiagramsequenceDiagram
participant User as 사용자
participant Page as OneToOneReviewPage
participant PosQuery as 긍정키워드쿼리
participant NegQuery as 부정키워드쿼리
participant ReviewQuery as 리뷰목록쿼리
participant Utils as buildEvaluationStatistics
participant Eval as EvaluationSection
participant List as ReviewList
User->>Page: 페이지 진입
Page->>PosQuery: 긍정 키워드 요청
Page->>NegQuery: 부정 키워드 요청
Page->>ReviewQuery: 리뷰 목록(무한스크롤) 요청
PosQuery-->>Page: 긍정 키워드 응답
NegQuery-->>Page: 부정 키워드 응답
ReviewQuery-->>Page: 리뷰 데이터 + totalCount
Page->>Utils: 키워드로 통계 생성
Utils-->>Page: statistics 반환
Page->>Eval: statistics 전달 (렌더)
Page->>List: 리뷰 배열 전달 (렌더)
User->>Page: 더보기 클릭
Page->>ReviewQuery: 다음 페이지 요청
ReviewQuery-->>Page: 추가 리뷰 데이터
Page->>List: 목록 갱신
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/app/(service)/(my)/my-study-review/one-to-one/_utils.ts (1)
3-5: 중복된 키워드 shape는interface로 추출해 두는 편이 낫습니다.Line 4-5에서 같은 object shape가 두 파라미터에 반복됩니다. 지금은 단순 매핑이라 괜찮지만, 응답 shape가 바뀌면 시그니처를 둘 다 수정해야 해서 유지보수 비용이 불필요하게 커집니다.
♻️ 제안
+interface EvaluationKeyword { + id: number; + content: string; + count: number; +} + export function buildEvaluationStatistics( - positiveKeywords: Array<{ id: number; content: string; count: number }>, - negativeKeywords: Array<{ id: number; content: string; count: number }>, + positiveKeywords: EvaluationKeyword[], + negativeKeywords: EvaluationKeyword[], ): GroupStudyReviewStatistics {As per coding guidelines 'Use interface for defining object shapes in TypeScript, not type aliases, when representing object structures.'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/one-to-one/_utils.ts around lines 3 - 5, Extract the repeated object shape into a shared interface (e.g., KeywordCount or EvaluationKeyword) and update the buildEvaluationStatistics signature to use Array<KeywordCount> for both positiveKeywords and negativeKeywords; update any other functions or type annotations that use the same inline shape to reference the new interface as well so future shape changes only need one edit (refer to buildEvaluationStatistics, positiveKeywords, negativeKeywords to locate usages).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(service)/(my)/my-study-review/one-to-one/[studySpaceId]/page.tsx:
- Around line 35-40: The positive/negative keyword queries are currently called
with empty params so their cache is shared across study pages; update the calls
to include the route param studySpaceId (e.g., call
useUserPositiveKeywordsQuery({ studySpaceId }) and useMyNegativeKeywordsQuery({
studySpaceId }) using the studySpaceId from the page params or useParams) so
their request and query key are scoped per-study; if scoping by studySpaceId
isn't supported by the hooks, change this section to a list-only view or extend
the hooks (e.g., add a studySpaceId param handling) so the statistics built by
buildEvaluationStatistics relate to the current study.
In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx:
- Around line 16-30: Stop coalescing the three query results to []/0 immediately
and instead branch on their loading/error states: read the status flags returned
by useUserPositiveKeywordsQuery, useMyNegativeKeywordsQuery and
useMyReviewsInfinityQuery (e.g., isLoading/isFetching/isPending and isError) and
only derive statistics = buildEvaluationStatistics(...) and reviews/totalCount
after a successful response; when any query isError show an inline error UI (and
trigger a toast via useToastStore in component code if needed) and when any
query is loading show a loading state—only treat keywords as [] or totalCount as
0 when the queries have succeeded with an empty payload. Ensure you update
references to statistics, reviews and totalCount to depend on the successful
result flags rather than unconditionally using positiveData?.keywords ?? [] /
reviewData?.totalCount ?? 0.
In `@src/types/schemas/group-study-form.schema.ts`:
- Line 33: The Zod call for studyLeaderParticipation is using an invalid string
argument; replace z.boolean('참여 여부를 체크해주세요') with a boolean schema that supplies
error messages via the options object (e.g., use z.boolean({ required_error: '참여
여부를 체크해주세요', invalid_type_error: '참여 여부를 체크해주세요' })) so the schema uses the Zod
v4 API correctly; update the studyLeaderParticipation declaration accordingly.
---
Nitpick comments:
In `@src/app/`(service)/(my)/my-study-review/one-to-one/_utils.ts:
- Around line 3-5: Extract the repeated object shape into a shared interface
(e.g., KeywordCount or EvaluationKeyword) and update the
buildEvaluationStatistics signature to use Array<KeywordCount> for both
positiveKeywords and negativeKeywords; update any other functions or type
annotations that use the same inline shape to reference the new interface as
well so future shape changes only need one edit (refer to
buildEvaluationStatistics, positiveKeywords, negativeKeywords to locate usages).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 70b06b1d-b3a4-42c9-9b8e-ae63d6d120a0
📒 Files selected for processing (8)
src/app/(service)/(my)/my-study-review/_components/completed-study-review-page.tsxsrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsxsrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/page.tsxsrc/app/(service)/(my)/my-study-review/one-to-one/[studySpaceId]/page.tsxsrc/app/(service)/(my)/my-study-review/one-to-one/_utils.tssrc/app/(service)/(my)/my-study-review/one-to-one/page.tsxsrc/components/common/modals/group-study-form-modal.tsxsrc/types/schemas/group-study-form.schema.ts
| const { data: positiveData } = useUserPositiveKeywordsQuery({}); | ||
| const { data: negativeData } = useMyNegativeKeywordsQuery({}); | ||
|
|
||
| const statistics = buildEvaluationStatistics( | ||
| positiveData?.keywords ?? [], | ||
| negativeData?.keywords ?? [], |
There was a problem hiding this comment.
상세 통계가 현재 스터디와 무관하게 동일하게 보입니다.
Line 35-40에서는 route param인 studySpaceId를 전혀 사용하지 않고 두 키워드 쿼리에 {}만 넘기고 있습니다. useUserPositiveKeywordsQuery / useMyNegativeKeywordsQuery는 전달한 params로 request와 query key를 만들기 때문에, 지금 구현이면 어떤 상세 페이지에 들어가도 같은 평가 통계가 캐시/재사용됩니다. 상세 페이지라면 현재 studySpaceId 기준으로 scope 하거나, 그게 불가능하면 이 섹션은 목록 전용으로 두는 편이 맞습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(service)/(my)/my-study-review/one-to-one/[studySpaceId]/page.tsx
around lines 35 - 40, The positive/negative keyword queries are currently called
with empty params so their cache is shared across study pages; update the calls
to include the route param studySpaceId (e.g., call
useUserPositiveKeywordsQuery({ studySpaceId }) and useMyNegativeKeywordsQuery({
studySpaceId }) using the studySpaceId from the page params or useParams) so
their request and query key are scoped per-study; if scoping by studySpaceId
isn't supported by the hooks, change this section to a list-only view or extend
the hooks (e.g., add a studySpaceId param handling) so the statistics built by
buildEvaluationStatistics relate to the current study.
| const { data: positiveData } = useUserPositiveKeywordsQuery({}); | ||
| const { data: negativeData } = useMyNegativeKeywordsQuery({}); | ||
| const { | ||
| data: reviewData, | ||
| fetchNextPage, | ||
| hasNextPage, | ||
| } = useMyReviewsInfinityQuery(); | ||
|
|
||
| const statistics = buildEvaluationStatistics( | ||
| positiveData?.keywords ?? [], | ||
| negativeData?.keywords ?? [], | ||
| ); | ||
|
|
||
| const reviews = reviewData?.reviews ?? []; | ||
| const totalCount = reviewData?.totalCount ?? 0; |
There was a problem hiding this comment.
초기/실패 상태가 빈 데이터로 오인됩니다.
Line 16-30에서 새로 추가한 세 쿼리 결과를 바로 [] / 0으로 치환하고 있어서, 초기 로딩이나 요청 실패 때도 아래 섹션이 실제 빈 상태처럼 렌더링됩니다. 이 페이지는 평가 통계와 후기 수를 새로 보여주기 시작한 만큼, isPending/isError를 먼저 분기하고 empty message는 정상 응답이 비어 있을 때만 노출하는 편이 안전합니다.
As per coding guidelines 'For recoverable failures in UI: use inline error display first, Toast as secondary. Never use browser alert(). Use useToastStore for Toast display in React components and getState() outside React.'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx around lines 16 -
30, Stop coalescing the three query results to []/0 immediately and instead
branch on their loading/error states: read the status flags returned by
useUserPositiveKeywordsQuery, useMyNegativeKeywordsQuery and
useMyReviewsInfinityQuery (e.g., isLoading/isFetching/isPending and isError) and
only derive statistics = buildEvaluationStatistics(...) and reviews/totalCount
after a successful response; when any query isError show an inline error UI (and
trigger a toast via useToastStore in component code if needed) and when any
query is loading show a loading state—only treat keywords as [] or totalCount as
0 when the queries have succeeded with an empty payload. Ensure you update
references to statistics, reviews and totalCount to depend on the successful
result flags rather than unconditionally using positiveData?.keywords ?? [] /
reviewData?.totalCount ?? 0.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/types/schemas/group-study-form.schema.ts (1)
150-160:⚠️ Potential issue | 🟠 Major프리미엄 스터디 가격 검증에서 숫자 형식 체크 누락
Number(data.price)가NaN인 경우priceNum < 10000은false를 반환하므로,"abc"같은 비숫자 값이 검증을 통과합니다. 이는 프리미엄 스터디에서만 발생하는 문제로, 나중에buildBasicInfoCommon()에서Number(v.price) || 0으로 처리되어 잘못된 입력이 조용히 무료 참가비(0)로 변환됩니다.최소 금액 비교 전에
Number.isFinite(priceNum)검사를 추가하여 유효한 숫자 형식임을 먼저 확인해야 합니다.🔧 수정 예시
return (data: GroupStudyBaseData, ctx: z.RefinementCtx) => { const priceNum = Number(data.price); // 프리미엄 스터디: 10,000원 이상 필수 if (data.classification === 'PREMIUM_STUDY') { - if (!data.price || priceNum < 10000) { + if (!data.price || !Number.isFinite(priceNum) || priceNum < 10000) { ctx.addIssue({ code: 'custom', message: '참가비는 10,000원 이상이어야 합니다.', path: ['price'], }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/types/schemas/group-study-form.schema.ts` around lines 150 - 160, In buildGroupStudyRefine, the premium-study price check uses priceNum < 10000 but doesn't guard against non-numeric inputs (Number(data.price) can be NaN), so add a Number.isFinite(priceNum) check before comparing min value and if it's not finite add a ctx.addIssue for the 'price' path indicating the price must be a valid number; then only perform the >=10000 validation when priceNum is a finite number. Also ensure this validation occurs before any downstream conversion like buildBasicInfoCommon's Number(v.price) || 0 can silently coerce invalid input to 0.
🧹 Nitpick comments (1)
src/types/schemas/group-study-form.schema.ts (1)
139-139: 객체 shape 추론 타입은interface로 통일해 주세요.
GroupStudyBaseData는 refine 입력 객체의 구조를 표현하는 타입이라서, 저장소 규칙상type별칭보다interface가 더 맞습니다.♻️ 정리 예시
-type GroupStudyBaseData = z.infer<typeof GroupStudyBaseObjectSchema>; +interface GroupStudyBaseData extends z.infer<typeof GroupStudyBaseObjectSchema> {}As per coding guidelines, 객체 구조를 나타내는 TypeScript 타입은
typealias가 아니라interface를 사용해야 합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/types/schemas/group-study-form.schema.ts` at line 139, Change the object-shape inference from a type alias to an interface: replace the type alias named GroupStudyBaseData (currently defined as z.infer<typeof GroupStudyBaseObjectSchema>) with an interface declaration of the same name that represents the inferred shape from GroupStudyBaseObjectSchema; update any usage sites that expect GroupStudyBaseData to continue to work with the new interface declaration (keep the same identifier, ensure imports/exports remain correct, and preserve the link to GroupStudyBaseObjectSchema used for deriving the shape in comments or docs if needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/types/schemas/group-study-form.schema.ts`:
- Around line 172-185: The validation currently enforces a minimum start date
even in edit mode; change the check in the ISO_DATE_REGEX block so that when
startDateMode === 'edit' you allow keeping the original start date or allow past
dates: obtain the original start date (e.g., originalStartDate passed into the
validator or available on ctx/data), and only call ctx.addIssue for
data.startDate < minYmd when NOT in edit mode OR when in edit mode and
data.startDate is different from originalStartDate (i.e., only validate a
changed startDate against minYmd); update the error message branch accordingly
and reference ISO_DATE_REGEX, getKoreaDate, toYmd, startDateMode,
data.startDate, and ctx.addIssue to locate where to implement this conditional.
- Around line 30-37: The edit form validation fails because
GroupStudyEditFormSchema inherits GroupStudyBaseObjectSchema which requires
type: z.enum(STUDY_TYPES), but refineStudyDetail() can convert legacy records to
have type === undefined; update the edit schema to accept missing type by making
the type field optional (change the type declaration in
GroupStudyBaseObjectSchema or specifically in GroupStudyEditFormSchema to
.optional()), or alternatively modify refineStudyDetail() to return a valid
default (e.g., 'PROJECT') instead of undefined—pick one approach and apply it
consistently so legacy records validate when opening the edit form.
---
Outside diff comments:
In `@src/types/schemas/group-study-form.schema.ts`:
- Around line 150-160: In buildGroupStudyRefine, the premium-study price check
uses priceNum < 10000 but doesn't guard against non-numeric inputs
(Number(data.price) can be NaN), so add a Number.isFinite(priceNum) check before
comparing min value and if it's not finite add a ctx.addIssue for the 'price'
path indicating the price must be a valid number; then only perform the >=10000
validation when priceNum is a finite number. Also ensure this validation occurs
before any downstream conversion like buildBasicInfoCommon's Number(v.price) ||
0 can silently coerce invalid input to 0.
---
Nitpick comments:
In `@src/types/schemas/group-study-form.schema.ts`:
- Line 139: Change the object-shape inference from a type alias to an interface:
replace the type alias named GroupStudyBaseData (currently defined as
z.infer<typeof GroupStudyBaseObjectSchema>) with an interface declaration of the
same name that represents the inferred shape from GroupStudyBaseObjectSchema;
update any usage sites that expect GroupStudyBaseData to continue to work with
the new interface declaration (keep the same identifier, ensure imports/exports
remain correct, and preserve the link to GroupStudyBaseObjectSchema used for
deriving the shape in comments or docs if needed).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: cb0d11fd-e45d-43ce-ac66-cbdae3f3b31e
📒 Files selected for processing (1)
src/types/schemas/group-study-form.schema.ts
| const GroupStudyBaseObjectSchema = z.object({ | ||
| classification: z.enum(STUDY_CLASSIFICATION), | ||
| // 스터디 리더 참여 여부(1) | ||
| studyLeaderParticipation: z.boolean({ | ||
| error: '참여 여부를 체크해주세요', | ||
| }), | ||
| // 스터디 유형(1) | ||
| type: z.enum(STUDY_TYPES), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -e
echo "=== STUDY_TYPES definition ==="
fd 'group-study-const' src -x sed -n '1,200p'
echo
echo "=== edit modal resolver / default value mapping ==="
fd 'group-study-form-modal\.tsx$' src -x sed -n '1,260p'
echo
echo "=== documented legacy handling in group step ==="
fd 'step1-group\.tsx$' src -x sed -n '1,140p'
echo
echo "=== relevant references ==="
rg -n "MENTORING|STUDY_TYPES|GroupStudyEditFormSchema|zodResolver|defaultValues" --type ts --type tsx srcRepository: code-zero-to-one/study-platform-client
Length of output: 14274
🏁 Script executed:
cat -n src/types/schemas/group-study-form.schema.ts | head -100Repository: code-zero-to-one/study-platform-client
Length of output: 3420
🏁 Script executed:
sed -n '100,250p' src/types/schemas/group-study-form.schema.tsRepository: code-zero-to-one/study-platform-client
Length of output: 4125
🏁 Script executed:
sed -n '180,220p' src/types/schemas/group-study-form.schema.tsRepository: code-zero-to-one/study-platform-client
Length of output: 1164
Edit 폼이 레거시 classification='GROUP_STUDY', type='MENTORING' 레코드를 편집할 수 없습니다.
refineStudyDetail()이 이 경우 type: undefined으로 변환하지만, GroupStudyEditFormSchema는 type: z.enum(STUDY_TYPES) (선택사항 없음)을 요구합니다. 폼을 열자마자 validation이 실패해서 기존 데이터 수정이 불가능합니다.
해결책:
- Edit 스키마에서
type필드를.optional()로 변경하거나 refineStudyDetail()에서undefined대신 유효한 기본값(예:'PROJECT')을 반환하세요
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/types/schemas/group-study-form.schema.ts` around lines 30 - 37, The edit
form validation fails because GroupStudyEditFormSchema inherits
GroupStudyBaseObjectSchema which requires type: z.enum(STUDY_TYPES), but
refineStudyDetail() can convert legacy records to have type === undefined;
update the edit schema to accept missing type by making the type field optional
(change the type declaration in GroupStudyBaseObjectSchema or specifically in
GroupStudyEditFormSchema to .optional()), or alternatively modify
refineStudyDetail() to return a valid default (e.g., 'PROJECT') instead of
undefined—pick one approach and apply it consistently so legacy records validate
when opening the edit form.
There was a problem hiding this comment.
type 필드는 required 속성이야. optional로 변경하면 안돼.
There was a problem hiding this comment.
@HA-SEUNG-JEONG, 맞습니다 — type을 .optional()로 변경하는 것은 잘못된 접근이었습니다. 정정해 주셔서 감사합니다.
다만, 레거시 classification='GROUP_STUDY' & type='MENTORING' 레코드에 대한 원래의 우려는 여전히 존재합니다. type 필드가 required로 유지되는 경우, refineStudyDetail()이 type: undefined로 변환하면 수정 폼을 열자마자 z.enum(STUDY_TYPES) validation이 실패합니다.
이 경우 Option 2가 올바른 해결책입니다 — refineStudyDetail()에서 MENTORING을 undefined 대신 유효한 기본값(예: 'PROJECT')으로 대체하세요:
// refineStudyDetail() 내부 (혹은 동등한 변환 함수)
type: STUDY_TYPES.includes(rawType) ? rawType : 'PROJECT', // 레거시 MENTORING → fallback이렇게 하면 GroupStudyEditFormSchema의 type: z.enum(STUDY_TYPES) 제약은 그대로 유지되고, 레거시 레코드도 유효한 상태로 폼이 열립니다. 기존 learning(#7946dcdf)에도 이 경우의 처리가 필요하다고 기록되어 있습니다.
이 부분이 이미 다른 곳에서 처리되고 있다면 알려주시면 검토해 보겠습니다!
✏️ Learnings added
Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 441
File: src/types/schemas/group-study-form.schema.ts:30-37
Timestamp: 2026-03-24T06:57:30.620Z
Learning: In `code-zero-to-one/study-platform-client`, the `type` field in `GroupStudyBaseObjectSchema` (defined in `src/types/schemas/group-study-form.schema.ts`) is intentionally required (`z.enum(STUDY_TYPES)`) and must NOT be made optional. When handling legacy records with `type='MENTORING'` (not in STUDY_TYPES), the correct fix is to update the data-mapping function (e.g., `refineStudyDetail()`) to map the invalid value to a valid fallback (e.g., 'PROJECT') rather than `undefined`, so the schema stays strict.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 379
File: src/features/study/group/ui/step/step1-group.tsx:61-65
Timestamp: 2026-02-05T02:17:57.573Z
Learning: In the repository `code-zero-to-one/study-platform-client`, there are existing database records where `classification='GROUP_STUDY'` and `type='MENTORING'`. When editing such records in `src/features/study/group/ui/step/step1-group.tsx`, the form must handle the case where the loaded type value is filtered out from the available options.
Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 435
File: src/hooks/common/use-reminder-review.tsx:0-0
Timestamp: 2026-03-21T07:12:50.468Z
Learning: In `code-zero-to-one/study-platform-client`, `StudyReviewModalStateResponse.targetStudySpaceId` is typed as `number` (not `number | null`). Do not suggest `?? undefined` normalization for this field — it is unnecessary as there is no null case in the type.
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx:376-376
Timestamp: 2026-03-23T14:43:23.483Z
Learning: In `code-zero-to-one/study-platform-client`, the settlement (정산) feature is intentionally under development and not yet finalized. Do not flag the replacement of settlement verification status (완료/미완료) with placeholder text as a regression — the settlement contract between frontend and backend does not yet exist. This applies to files like `src/features/admin/mentoring/ui/mentor-applications-page-client.tsx`.
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/admin/mentoring/ui/mentor-applications-page-client.tsx:292-292
Timestamp: 2026-03-23T14:42:50.316Z
Learning: In `code-zero-to-one/study-platform-client`, the settlement (정산) contract/API integration is intentionally not yet implemented. The `settlementDraft` field and its `verified` status on `MentorSettings` should not be treated as a source of truth for settlement readiness. The admin UI in `src/features/admin/mentoring/ui/mentor-applications-page-client.tsx` intentionally shows a placeholder message ("서버 정산 readiness 계약 필요") for the settlement verification field until the server-side settlement contract is established. Do not flag this as a regression.
Learnt from: CR
Repo: code-zero-to-one/study-platform-client PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T13:45:27.769Z
Learning: Applies to **/{mentoring,premium-study,group-study}/**/*.{ts,tsx} : Distinguish between mentoring (1:1 professional consultation, `/mentoring/*` routes, `/api/v1/mentors` endpoint, no assignments) and mentor-study (premium group study, `/premium-study/*` routes, `/api/v1/group-studies` endpoint, includes assignments/evaluation).
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/mentoring/model/mentor-registration-preview.ts:286-294
Timestamp: 2026-03-23T14:43:26.562Z
Learning: In `src/features/mentoring/model/mentor-registration-preview.ts` `getChangedSections`, only `companyName` and `hideCompanyName` need to be compared for the `headline` highlight section — `companyCategory` does NOT affect the headline display in `src/features/mentoring/ui/detail/mentor-detail-page.tsx`.
There was a problem hiding this comment.
rawType은 대체 뭐야?
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n "refineStudyDetail" --type ts --type tsx -C5
Length of output: 27
🏁 Script executed:
#!/bin/bash
rg -n "refineStudyDetail" -C5
Length of output: 2542
🏁 Script executed:
#!/bin/bash
sed -n '125,165p' src/components/common/modals/group-study-form-modal.tsx
Length of output: 1683
@HA-SEUNG-JEONG, 죄송합니다 — rawType은 실제 코드에 없는 제가 임의로 만든 예시 변수명이었습니다. 혼란을 드렸습니다.
실제 코드(src/components/common/modals/group-study-form-modal.tsx)를 확인하니, 해당 변수는 originalType / refinedType입니다. 구체적인 수정 위치는 Line 132~136입니다:
// 현재 (문제 있는 코드)
let refinedType = originalType;
if (
refinedClassification === 'GROUP_STUDY' &&
originalType === 'MENTORING'
) {
refinedType = undefined; // ← GroupStudyEditFormSchema 검증 실패 원인
}// 수정 후
let refinedType = originalType;
if (
refinedClassification === 'GROUP_STUDY' &&
originalType === 'MENTORING'
) {
refinedType = 'PROJECT'; // 레거시 MENTORING → 유효한 기본값으로 fallback
}refinedType = undefined이 그대로 type 필드에 반환되기 때문에, GroupStudyEditFormSchema의 z.enum(STUDY_TYPES) 검증이 폼을 열자마자 실패합니다. 'PROJECT'로 대체하면 스키마는 strict하게 유지되면서 레거시 레코드도 정상적으로 편집할 수 있습니다.
🧠 Learnings used
Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 441
File: src/types/schemas/group-study-form.schema.ts:30-37
Timestamp: 2026-03-24T06:57:30.620Z
Learning: In `code-zero-to-one/study-platform-client`, the `type` field in `GroupStudyBaseObjectSchema` (defined in `src/types/schemas/group-study-form.schema.ts`) is intentionally required (`z.enum(STUDY_TYPES)`) and must NOT be made optional. When handling legacy records with `type='MENTORING'` (not in STUDY_TYPES), the correct fix is to update the data-mapping function (e.g., `refineStudyDetail()`) to map the invalid value to a valid fallback (e.g., 'PROJECT') rather than `undefined`, so the schema stays strict.
Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 379
File: src/features/study/group/ui/step/step1-group.tsx:61-65
Timestamp: 2026-02-05T02:17:57.573Z
Learning: In the repository `code-zero-to-one/study-platform-client`, there are existing database records where `classification='GROUP_STUDY'` and `type='MENTORING'`. When editing such records in `src/features/study/group/ui/step/step1-group.tsx`, the form must handle the case where the loaded type value is filtered out from the available options.
Learnt from: HA-SEUNG-JEONG
Repo: code-zero-to-one/study-platform-client PR: 435
File: src/hooks/common/use-reminder-review.tsx:0-0
Timestamp: 2026-03-21T07:12:50.468Z
Learning: In `code-zero-to-one/study-platform-client`, `StudyReviewModalStateResponse.targetStudySpaceId` is typed as `number` (not `number | null`). Do not suggest `?? undefined` normalization for this field — it is unnecessary as there is no null case in the type.
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/mentoring/ui/registration/mentor-career-entries-editor.tsx:376-376
Timestamp: 2026-03-23T14:43:23.483Z
Learning: In `code-zero-to-one/study-platform-client`, the settlement (정산) feature is intentionally under development and not yet finalized. Do not flag the replacement of settlement verification status (완료/미완료) with placeholder text as a regression — the settlement contract between frontend and backend does not yet exist. This applies to files like `src/features/admin/mentoring/ui/mentor-applications-page-client.tsx`.
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/admin/mentoring/ui/mentor-applications-page-client.tsx:292-292
Timestamp: 2026-03-23T14:42:50.316Z
Learning: In `code-zero-to-one/study-platform-client`, the settlement (정산) contract/API integration is intentionally not yet implemented. The `settlementDraft` field and its `verified` status on `MentorSettings` should not be treated as a source of truth for settlement readiness. The admin UI in `src/features/admin/mentoring/ui/mentor-applications-page-client.tsx` intentionally shows a placeholder message ("서버 정산 readiness 계약 필요") for the settlement verification field until the server-side settlement contract is established. Do not flag this as a regression.
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/mentoring/model/mentor-registration-preview.ts:286-294
Timestamp: 2026-03-23T14:43:26.562Z
Learning: In `src/features/mentoring/model/mentor-registration-preview.ts` `getChangedSections`, only `companyName` and `hideCompanyName` need to be compared for the `headline` highlight section — `companyCategory` does NOT affect the headline display in `src/features/mentoring/ui/detail/mentor-detail-page.tsx`.
Learnt from: CR
Repo: code-zero-to-one/study-platform-client PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T13:45:27.769Z
Learning: Applies to **/{mentoring,premium-study,group-study}/**/*.{ts,tsx} : Distinguish between mentoring (1:1 professional consultation, `/mentoring/*` routes, `/api/v1/mentors` endpoint, no assignments) and mentor-study (premium group study, `/premium-study/*` routes, `/api/v1/group-studies` endpoint, includes assignments/evaluation).
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/mentoring/ui/mentor-directory/mentor-card.tsx:158-158
Timestamp: 2026-03-23T14:43:31.762Z
Learning: In `src/features/mentoring/model/mentor-profile-utils.ts`, `skillTags` normalization uses `source?.skillTags ?? []` without `toStringArray`, which is slightly inconsistent, but API responses are already normalized upstream in `src/features/mentoring/api/mentor-profile.mapper.ts` (around line 350) and `src/features/mentoring/api/mentor-settings.mapper.ts` (around line 404). Do not flag this as a critical normalization gap or claim that invalid API responses pass through raw; the upstream mappers already sanitize the data.
Learnt from: Hyeonjun0527
Repo: code-zero-to-one/study-platform-client PR: 439
File: src/features/mentoring/ui/detail/mentor-detail-page.tsx:122-133
Timestamp: 2026-03-23T14:43:17.989Z
Learning: In code-zero-to-one/study-platform-client, `skillTags` in `src/features/mentoring/model/mentor-profile-utils.ts` uses `source?.skillTags ?? []` directly without `toStringArray` normalization. This is safe because the API response is already normalized upstream in `src/features/mentoring/api/mentor-profile.mapper.ts` and `src/features/mentoring/api/mentor-settings.mapper.ts` before reaching this point. Do not flag this as a normalization bypass — the normalization boundary is at the mapper layer.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/app/(service)/(my)/my-study-review/one-to-one/page.tsx (1)
24-30:⚠️ Potential issue | 🟠 Major로딩/실패 상태가 빈 통계·빈 후기로 보입니다.
여기서 바로
[]/0으로 치환해버리면 초기 로딩이나 요청 실패 때도EvaluationSection과 후기 섹션이 실제 빈 상태처럼 렌더링됩니다. 성공 응답이 확인된 뒤에만 기본값을 계산하고, 실패는 인라인 에러로 먼저 분기하는 편이 안전합니다.As per coding guidelines 'For recoverable failures in UI: use inline error display first, Toast as secondary. Never use browser
alert(). UseuseToastStorefor Toast display in React components andgetState()outside React.'Also applies to: 49-58
🧹 Nitpick comments (1)
src/app/(service)/(my)/my-study-review/one-to-one/page.tsx (1)
9-13: 공용 UI import를 shared 경로 +@/alias로 정리해주세요.
EvaluationSection을 group route 전용 폴더에서 끌어오고 나머지도 상대경로로 섞어 쓰면, route 구조를 바꿀 때 one-to-one 페이지가 같이 깨지기 쉬워집니다. 공용 컴포넌트라면 shared 위치로 옮기고 import도 alias로 통일하는 편이 안전합니다.As per coding guidelines 'Component path aliases: use
@/prefix mapped to./src/(configured in tsconfig.json) for all imports.'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx around lines 9 - 13, The imports in page.tsx mix relative routes and a group-specific path which can break when routes change; move any truly shared components (e.g., EvaluationSection) into the shared location and update all imports in this file to use the '@/...' alias (for example replace the group route import of EvaluationSection with an alias import from the shared components folder) and normalize the other imports (OneToOneReviewCard, CompletedStudyReviewPage, StudyReviewTabNav, buildEvaluationStatistics) to use '@/...' paths so the page depends only on shared modules rather than route-specific folders.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx:
- Around line 16-22: The three data hooks on the page
(useUserPositiveKeywordsQuery, useMyNegativeKeywordsQuery,
useMyReviewsInfinityQuery) are called without a studyType filter so they return
mixed study types; update each call to pass the studyType prop used by
CompletedStudyReviewPage (studyType: "ONE_ON_ONE_STUDY" or
studyType="ONE_ON_ONE_STUDY") so only 1:1 review data is fetched, and adjust any
call signatures if necessary to accept that parameter (ensure
useMyReviewsInfinityQuery receives the same studyType argument for pagination
behavior).
In `@src/components/common/modals/group-study-form-modal.tsx`:
- Around line 86-89: The edit-mode schema (GroupStudyEditFormSchema) allows
today as a valid start date but the Step1OpenGroupStudy date input enforces
min={formatKoreaYMD(addDays(getKoreaDate(), 1))} unconditionally, preventing
users from selecting the existing (today) date; update the Step1OpenGroupStudy
component (used in src/components/forms/group-study-steps/step1-group.tsx) to
accept and honor a mode or minDate prop (e.g., mode or minDate) and pass a
relaxed min (or undefined) when the parent form is using
editMethods/GroupStudyEditFormSchema so the UI date picker matches the edit
schema constraints.
---
Nitpick comments:
In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx:
- Around line 9-13: The imports in page.tsx mix relative routes and a
group-specific path which can break when routes change; move any truly shared
components (e.g., EvaluationSection) into the shared location and update all
imports in this file to use the '@/...' alias (for example replace the group
route import of EvaluationSection with an alias import from the shared
components folder) and normalize the other imports (OneToOneReviewCard,
CompletedStudyReviewPage, StudyReviewTabNav, buildEvaluationStatistics) to use
'@/...' paths so the page depends only on shared modules rather than
route-specific folders.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: edccb209-e9af-46ce-ac6a-e87d4fb5db55
📒 Files selected for processing (3)
src/app/(service)/(my)/my-study-review/one-to-one/_utils.tssrc/app/(service)/(my)/my-study-review/one-to-one/page.tsxsrc/components/common/modals/group-study-form-modal.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app/(service)/(my)/my-study-review/one-to-one/_utils.ts
| const { data: positiveData } = useUserPositiveKeywordsQuery({}); | ||
| const { data: negativeData } = useMyNegativeKeywordsQuery({}); | ||
| const { | ||
| data: reviewData, | ||
| fetchNextPage, | ||
| hasNextPage, | ||
| } = useMyReviewsInfinityQuery(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
hook_file="$(fd 'use-review-query\.(ts|tsx)$' src | head -n1)"
echo "Hook file: $hook_file"
echo
echo "== Hook definitions =="
rg -n -A20 -B5 'useUserPositiveKeywordsQuery|useMyNegativeKeywordsQuery|useMyReviewsInfinityQuery' "$hook_file"
echo
echo "== Review filter / endpoint references =="
rg -n -A4 -B4 'ONE_ON_ONE_STUDY|studyType|positiveKeywords|negativeKeywords|myReviews' src \
-g '!**/*.test.*'Repository: code-zero-to-one/study-platform-client
Length of output: 50397
🏁 Script executed:
# API 구현체 찾기
find src/api -name "*.ts" -type f | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 879
🏁 Script executed:
# use-review-query.ts 전체 내용 확인
wc -l src/hooks/queries/use-review-query.tsRepository: code-zero-to-one/study-platform-client
Length of output: 123
🏁 Script executed:
# 페이지 파일 확인
cat -n src/app/\(service\)/\(my\)/my-study-review/one-to-one/page.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 3193
🏁 Script executed:
# API 함수 구현 찾기
rg -n "getUserPositiveKeywords|getMyNegativeKeywords|getMyReviews" src/api -A10 | head -100Repository: code-zero-to-one/study-platform-client
Length of output: 9623
🏁 Script executed:
cat -n src/api/endpoints/review/get-review.tsRepository: code-zero-to-one/study-platform-client
Length of output: 3165
🏁 Script executed:
cat -n src/hooks/queries/use-review-query.tsRepository: code-zero-to-one/study-platform-client
Length of output: 5589
🏁 Script executed:
# API 타입 정의 확인
rg -n "UserPositiveKeywordsRequest|MyNegativeKeywordsRequest|MyReviewsRequest" src/types -A5Repository: code-zero-to-one/study-platform-client
Length of output: 1125
세 쿼리가 모든 스터디 타입의 데이터를 함께 조회하고 있습니다.
이 페이지는 1:1 리뷰만 표시해야 하지만, 호출하는 세 쿼리(useUserPositiveKeywordsQuery, useMyNegativeKeywordsQuery, useMyReviewsInfinityQuery)는 모두 studyType 파라미터가 없습니다. API 함수, 타입 정의, 엔드포인트 모두에서 1:1 필터링이 불가능하므로, 긍정 키워드, 부정 키워드, 후기 데이터가 그룹 스터디 리뷰와 함께 섞여 조회됩니다.
CompletedStudyReviewPage 컴포넌트처럼 studyType="ONE_ON_ONE_STUDY" 필터를 세 쿼리에도 추가해야 합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx around lines 16 -
22, The three data hooks on the page (useUserPositiveKeywordsQuery,
useMyNegativeKeywordsQuery, useMyReviewsInfinityQuery) are called without a
studyType filter so they return mixed study types; update each call to pass the
studyType prop used by CompletedStudyReviewPage (studyType: "ONE_ON_ONE_STUDY"
or studyType="ONE_ON_ONE_STUDY") so only 1:1 review data is fetched, and adjust
any call signatures if necessary to accept that parameter (ensure
useMyReviewsInfinityQuery receives the same studyType argument for pagination
behavior).
└ MENTOR_STUDY→PREMIUM_STUDY 역매핑, thumbnailExtension 안전 가드 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
SatisfactionSection을EvaluationSection으로 교체하고 키워드 통계 데이터 직접 주입CompletedStudyReviewPage에hideTabNav,hideEmptyMessageprops 추가해 1:1 리뷰 페이지에서 재사용 가능하도록 개선GroupStudyFormSchema)과 수정용(GroupStudyEditFormSchema)으로 분리 — 수정 시 시작일 최솟값이 "내일"이 아닌 "오늘"로 완화GroupStudyFormModal)에GroupStudyEditFormSchema적용Changes
Features
one-to-one/page.tsxone-to-one/[studySpaceId]/page.tsxcompleted-study-review-page.tsxBug Fixes
group-study-form.schema.tsgroup-study-form-modal.tsxTest plan
/my-study-review/one-to-one)에서 평가 통계와 후기 목록이 올바르게 노출되는지 확인/my-study-review/one-to-one/[id])에서 EvaluationSection이 올바르게 렌더링되는지 확인🤖 Generated with Claude Code
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정
스타일
리팩토링